package com.exam.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.exam.annotation.Cache;
import com.exam.entity.Answer;
import com.exam.entity.Question;
import com.exam.entity.QuestionBank;
import com.exam.mapper.AnswerMapper;
import com.exam.mapper.QuestionBankMapper;
import com.exam.mapper.QuestionMapper;
import com.exam.service.QuestionBankService;
import com.exam.utils.RedisUtil;
import com.exam.vo.BankHaveQuestionSum;
import com.exam.vo.PageResponse;
import com.exam.vo.QuestionVo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.exam.utils.CommonUtils.setLikeWrapper;

/**
 * @Author 小聋包
 * @Date 2024/4/19 8:55
 * @Version 1.0
 */
@Service
@RequiredArgsConstructor
public class QuestionBankServiceImpl extends ServiceImpl<QuestionBankMapper, QuestionBank> implements QuestionBankService {

    private final  QuestionBankMapper questionBankMapper;

    private final QuestionMapper questionMapper;

    private final AnswerMapper answerMapper;
    private final RedisUtil redisUtil;

    /**
     * 根据题库名称、页码和每页条数获取题库题目数量统计
     *
     * @param bankName 题库名称
     * @param pageNo 页码
     * @param pageSize 每页条数
     * @return 返回分页后的题目数量统计信息
     */
    @Override
    public PageResponse<BankHaveQuestionSum> getBankHaveQuestionSumByType(String bankName, Integer pageNo, Integer pageSize) {
        // 创建查询条件包装器
        QueryWrapper<QuestionBank> wrapper=new QueryWrapper<>();
        // 设置模糊查询条件，匹配题库名称
        setLikeWrapper(wrapper, Collections.singletonMap("bank_name",bankName));
        // 执行分页查询，获取题库信息
        IPage<QuestionBank> iPage=questionBankMapper.selectPage(new Page<>(pageNo,pageSize),wrapper);
        // 获取查询结果中的题库列表
        List<QuestionBank> questionBanks=iPage.getRecords();

        // //封装成传给前端的数据类型
        // 初始化用于存储题目数量统计信息的列表
        List<BankHaveQuestionSum> bankHaveQuestionSums=new ArrayList<>();

        // 遍历题库列表
        for (QuestionBank questionBank: questionBanks){
            // 创建用于存储题目数量统计信息的VO对象
            //创建vo对象
            BankHaveQuestionSum bankHaveQuestionSum=new BankHaveQuestionSum();
            // 设置VO对象的属性
            //设置属性
            bankHaveQuestionSum.setQuestionBank(questionBank);

            // 查询单选题数量
            //设置单选题的数量
            Integer singleQuestionCount=questionMapper.selectCount(new QueryWrapper<Question>().eq("qu_type",1).like("qu_bank_name",questionBank.getBankName()));
            bankHaveQuestionSum.setSingleChoice(singleQuestionCount);

            // 查询多选题数量
            //设置多选题数量
            Integer multipleQuestionCount=questionMapper.selectCount(new QueryWrapper<Question>().eq("qu_type",2).like("qu_bank_name",questionBank.getBankName()));
            bankHaveQuestionSum.setMultipleChoice(multipleQuestionCount);

            // 查询判断题数量
            //设置判断题的数量
            Integer judgeQuestionCount=questionMapper.selectCount(new QueryWrapper<Question>().eq("qu_type",3).like("qu_bank_name",questionBank.getBankName()));
            bankHaveQuestionSum.setJudge(judgeQuestionCount);

            // 查询简答题数量
            //设置简答题数量
            Integer shortAnswerQuestionCount=questionMapper.selectCount(new QueryWrapper<Question>().eq("qu_type",4).like("qu_bank_name",questionBank.getBankName()));
            bankHaveQuestionSum.setShortAnswer(shortAnswerQuestionCount);

            // 将题目数量统计信息添加到列表中
            //加入list中
            bankHaveQuestionSums.add(bankHaveQuestionSum);
        }

        // 返回分页后的题目数量统计信息
        return PageResponse.<BankHaveQuestionSum>builder()
                .data(bankHaveQuestionSums)
                .total(iPage.getTotal())
                .build();
    }


    /**
     * 根据题库ID获取题目列表
     *
     * @param bankId 题库ID
     * @return 题目列表
     */
    @Cache(prefix = "questionBankQuestion",suffix = "#bankId",ttl = 6,randomTime = 1,timeUnit = TimeUnit.HOURS)
    @Override
    public List<QuestionVo> getQuestionByBankId(Integer bankId) {
        // 根据题库ID查询题库信息
        QuestionBank bank=questionBankMapper.selectById(bankId);

        // 在题库中查询(单选,多选,判断题)题目
        //在题库中的(单选,多选,判断题)题目
        List<Question> questions=questionMapper.selectList(new QueryWrapper<Question>().like("qu_bank_name",bank.getBankName()).in("qu_type",1,2,3));

        // 构造前端需要的Vo对象列表
        //构造前端需要Vo对象
        List<QuestionVo> questionVos=new ArrayList<>();

        for (Question question:questions) {
            // 创建Vo对象
            QuestionVo questionVo = new QuestionVo();
            questionVo.setQuestionId(question.getId());
            questionVo.setQuestionLevel(question.getLevel());

            // 设置图片信息（如果图片不为空）
            if (question.getImage() != null && !question.getImage().equals(""))//防止没有图片对象
            {
                questionVo.setImages(question.getImage().split(","));
            }

            // 设置其他题目信息
            questionVo.setCreatePerson(question.getCreatePerson());
            questionVo.setAnalysis(question.getAnalysis());
            questionVo.setQuestionContent(question.getQuContent());
            questionVo.setQuestionType(question.getQuType());

            // 查询答案信息
            Answer answer = answerMapper.selectOne(new QueryWrapper<Answer>().eq("question_id", question.getId()));

            // 获取选项个数
            //选项个数
            String[] options = answer.getAllOption().split(",");
            String[] images = answer.getImages().split(",");

            // 构造答案对象数组
            //构造答案对象
            QuestionVo.Answer[] handleAnswer = new QuestionVo.Answer[options.length];

            // 处理每个选项
            //字段处理
            for (int i = 0; i < options.length; i++) {
                // 创建答案对象
                QuestionVo.Answer answer1 = new QuestionVo.Answer();

                // 设置选项图片（如果图片不为空）
                if (images.length - 1 >= i && images[i] != null && !images[i].equals("")) {
                    answer1.setImages(new String[]{images[i]});
                }

                // 设置选项内容
                answer1.setAnswer(options[i]);

                // 设置选项索引
                answer1.setId(i);

                // 设置是否为正确答案（默认为false）
                answer1.setIsTrue("false");

                // 将答案对象添加到答案数组中
                handleAnswer[i] = answer1;
            }

            // 根据题目类型设置正确答案
            if (question.getQuType() != 2) {//单选和判断
                // 单选和判断题，只有一个正确答案
                int trueOption = Integer.parseInt(answer.getTrueOption());
                handleAnswer[trueOption].setIsTrue("true");
                handleAnswer[trueOption].setAnalysis(answer.getAnalysis());
            } else {
                // 多选题，有多个正确答案
                //多选
                String[] trueOptions = answer.getTrueOption().split(",");
                for (String trueOption : trueOptions) {
                    // 设置每个正确答案的属性
                    handleAnswer[Integer.parseInt(trueOption)].setIsTrue("true");
                    handleAnswer[Integer.parseInt(trueOption)].setAnalysis(answer.getAnalysis());
                }
            }

            // 将答案数组设置到Vo对象中，并添加到Vo对象列表中
            questionVo.setAnswer(handleAnswer);
            questionVos.add(questionVo);
        }

        // 返回题目列表
        return questionVos;
    }
    /**
     * 根据题库ID和题目类型获取题目列表
     *
     * @param bankId 题库ID
     * @param type   题目类型
     * @return 题目列表
     */
    @Override
    public List<QuestionVo> getQuestionByBankIdAndType(Integer bankId, Integer type) {
        // 根据题库ID获取题目列表
        List<QuestionVo> questionVoList = getQuestionByBankId(bankId);

        // 移除与题目类型不符的题目
        questionVoList.removeIf(questionVo -> !Objects.equals(questionVo.getQuestionType(), type));

        return questionVoList;
    }

    /**
     * 获取所有题库信息列表
     *
     * @return 题库信息列表
     * @Cache(prefix = "questionBanks", ttl = 10, timeUnit = TimeUnit.HOURS) 缓存注解，缓存前缀为"questionBanks"，缓存时间为10小时
     */
    @Cache(prefix = "questionBanks", ttl = 10, timeUnit = TimeUnit.HOURS)
    @Override
    public List<QuestionBank> getAllQuestionBanks() {
        // 调用questionBankMapper的selectList方法，传入null作为参数，获取所有题库信息列表
        return questionBankMapper.selectList(null);
    }


    /**
     * 将题目添加到题库中
     *
     * @param questionIds 要添加的问题ID列表，多个ID用逗号分隔
     * @param banks       要将问题添加的题库ID列表，多个ID用逗号分隔
     */
    @Override
    public void addQuestionToBank(String questionIds, String banks) {
        // 需要操作的问题
        String[] quIds = questionIds.split(",");
        // 需要放入的题库id
        String[] bankIds = banks.split(",");

        // 将每一个题目放入每一个题库中
        for (String quId : quIds) {
            // 当前的问题对象
            Question question = questionMapper.selectById(Integer.parseInt(quId));
            String quBankId = question.getQuBankId();
            // 当前已经有的题库id
            String[] qid = quBankId.split(",");
            System.out.println(quBankId);
            // 存在去重后的题库id
            Set<Integer> allId = new HashSet<>();
            if (!quBankId.equals("")) {// 防止题目没有题库
                for (String s : qid) {
                    allId.add(Integer.parseInt(s));
                }
            }
            // 将新增的仓库id放入
            for (String bankId : bankIds) {
                // remove cache
                redisUtil.del("questionBankQuestion:" + bankId);
                allId.add(Integer.parseInt(bankId));
            }
            // 处理后的id字符串 例如(1,2,3)
            String handleHaveBankIds = allId.toString().replaceAll(" ", "");
            handleHaveBankIds = handleHaveBankIds.substring(1, handleHaveBankIds.length() - 1);
            // 更新当前用户的题库id值
            question.setQuBankId(handleHaveBankIds);

            // 将存放处理后的set集合遍历,然后替换数据库的题库名
            StringBuilder bankNames = new StringBuilder();
            for (Integer id : allId) {
                bankNames.append(questionBankMapper.selectById(id).getBankName()).append(",");
            }
            // 替换原来的仓库名称
            question.setQuBankName(bankNames.substring(0, bankNames.toString().length() - 1));
            questionMapper.update(question, new UpdateWrapper<Question>().eq("id", question.getId()));
        }
    }

    /**
     * 从题库中移除问题
     *
     * @param questionIds 要移除的问题ID列表，多个ID用逗号分隔
     * @param banks       要移除的题库ID列表，多个ID用逗号分隔
     */
    @Override
    public void removeBankQuestion(String questionIds, String banks) {
        // 需要操作的问题
        String[] quIds = questionIds.split(",");
        // 需要移除的题库id
        String[] bankIds = banks.split(",");
        // 操作需要移除仓库的问题
        for (String quId : quIds) {
            Question question = questionMapper.selectById(Integer.parseInt(quId));
            String quBankId = question.getQuBankId();
            // 当前问题拥有的仓库id
            String[] curHaveId = quBankId.split(",");
            // 存储处理后的id
            Set<Integer> handleId = new HashSet<>();
            if (!quBankId.equals("")) {
                for (String s : curHaveId) {
                    handleId.add(Integer.parseInt(s));
                }
            }
            // 遍历查询set中是否含有需要删除的仓库id
            for (String bankId : bankIds) {
                // remove cache
                redisUtil.del("questionBankQuestion:" + bankId);
                handleId.remove(Integer.parseInt(bankId));
            }
            // 处理后的id字符串 例如(1,2,3)
            String handleHaveBankIds = handleId.toString().replaceAll(" ", "");
            handleHaveBankIds = handleHaveBankIds.substring(1, handleHaveBankIds.length() - 1);
            // 更新当前用户的题库id值
            question.setQuBankId(handleHaveBankIds);

            if (!handleHaveBankIds.equals("")) {// 删除后还存在剩余的题库
                // 将存放处理后的set集合遍历,然后替换数据库的题库名
                StringBuilder bankNames = new StringBuilder();
                for (Integer id : handleId) {
                    bankNames.append(questionBankMapper.selectById(id).getBankName()).append(",");
                }
                // 替换原来的仓库名称
                question.setQuBankName(bankNames.substring(0, bankNames.toString().length() - 1));
            } else {// 不剩题库了
                question.setQuBankName("");
            }
            // 更新问题对象
            questionMapper.update(question, new UpdateWrapper<Question>().eq("id", question.getId()));
        }
    }

    /**
     * 从数据库中删除指定id的题库
     *
     * @param ids 题库id列表，多个id使用逗号分隔
     * @Transactional 用于标记需要开启事务的方法
     * @Override 用于标记重写父类方法
     */
    @Transactional
    @Override
    public void deleteQuestionBank(String ids) {
        // 将传入的ids字符串按照逗号分割成题库id数组
        String[] bankId = ids.split(",");
        for (String s : bankId) {
            // 根据题库id找到题库对象
            // 找到题库
            QuestionBank questionBank = questionBankMapper.selectById(s);
            // 根据题库名称查找与此题库相关的所有问题信息
            // 找到与此题库相关的所有问题信息
            List<Question> questions = questionMapper.selectList(new QueryWrapper<Question>().like("qu_bank_name", questionBank.getBankName()));
            // 遍历问题列表，移除与此题库相关的信息
            for (Question question : questions) {
                // 获取题目当前所属的题库名和题库id
                String quBankName = question.getQuBankName();
                String quBankId = question.getQuBankId();
                // 将题库名和题库id按照逗号分割成数组
                String[] name = quBankName.split(",");
                String[] id = quBankId.split(",");
                // 创建新的题库名数组和题库id数组，长度为原数组长度减1
                // 新的题库名
                String[] newName = new String[name.length - 1];
                // 新的题库id数据
                String[] newId = new String[id.length - 1];

                // 遍历题库名数组，排除要删除的题库名，将剩余的题库名存入新的题库名数组
                for (int i = 0, j = 0; i < name.length; i++) {
                    if (!name[i].equals(questionBank.getBankName())) {
                        newName[j] = name[i];
                        j++;
                    }
                }
                // 遍历题库id数组，排除要删除的题库id，将剩余的题库id存入新的题库id数组
                for (int i = 0, j = 0; i < id.length; i++) {
                    if (!id[i].equals(String.valueOf(questionBank.getBankId()))) {
                        newId[j] = id[i];
                        j++;
                    }
                }
                // 将新的题库名数组和题库id数组转换成字符串，并去掉空格、方括号
                String handleName = Arrays.toString(newName).replaceAll(" ", "").replaceAll("]", "").replace("[", "");
                String handleId = Arrays.toString(newId).replaceAll(" ", "").replaceAll("]", "").replace("[", "");
                // 更新题目对象的题库名和题库id字段
                // 设置删除题库后的新字段
                question.setQuBankName(handleName);
                question.setQuBankId(handleId);
                // 更新题目信息
                // 更新题目
                questionMapper.update(question, new UpdateWrapper<Question>().eq("id", question.getId()));
                // 清除题目缓存
                redisUtil.del("questionVo:" + question.getId());
            }
            // 删除题库信息
            // 删除题库
            questionBankMapper.deleteById(Integer.parseInt(s));
            // 清除题库缓存
            // 清楚缓存
            redisUtil.del("questionBankQuestion:" + s, "questionBanks");
        }
    }

    /**
     * 将题库信息添加到数据库中
     *
     * @param questionBank 要添加的题库信息
     */
    @Override
    public void addQuestionBank(QuestionBank questionBank) {
        // 调用questionBankMapper的insert方法，将题库信息插入到数据库中
        questionBankMapper.insert(questionBank);
        // 调用redisUtil的del方法，删除缓存中的"questionBanks"键
        redisUtil.del("questionBanks");
    }

}
